#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
/*#include <glib.h>*/
#include "rand.h"

#define INFINIT 100000.0
/*NOTA: buscar el maximo double representable*/




/*
	Generacion de VA Exponencial
 */
static double generar_exponencial(double tr)
{
	long seed = -1;
	double x = 0.0;
	double u = ran2(&seed);
	x = -1.0/(double)(tr*log(u));
	
	return x;
}

/*
	Generacion de VA Poisson
 */
static int proceso_poisson(double tf)
{
	long seed = -1;
	int i = 0;
	double p = 0.0;
	double F = 0.0;
	double u = 0.0;
	
	p = exp(-tf);
	F = p;
	u = ran2(&seed);
	while( u >= F)
	{
		p = (tf*p)/(i+1);
		F += p; 
		++i;
	}
	return i;
}

/*
	Se cargan los tiempos de fallas para los t1, t2, tn, t* tiempos
	para las N + S maquinas y el tiempop tr
 */
/*TODO */
static GQueue cargar_tiempos(Gqueue tiempos, int M, double tf)
{
	int i; 
	
	for(i = 0 ; i < M ; i++)
	{
		
		tiempos->enqueue(proceso_poisson(1/tf)));
	}
	/* en el ultimo lugar de la cola agrego al tiempo para tr
	 que inicialmente es INF
	 */
	tiempos->enqueue(INFINIT);
	tiempos->sort();
	return tiempos;
}


static double obtener_tiempo_reparacion(Qqueue tiempos)
{
	return obtener_ultimo(tiempos);
}

static double obtener_menor_tiempo(GQueue tiempos)
{
	return obtener_primer(tiempos);
}

Gqueue crear_tiempos(int total_tiempos)
{
	return crear_cola(total_tiempos);/*o createQueue  o como lo llame   clau*/
}

/*
	funcion que calcula el tiempo de funcionamiento del sistema.
	Calcula tiempo siempre y cuando el sistema sea operativo
	O sea mientras no halla mas de S maquinas en reparacion
	En caso contrario termina.
	
	N: numero de maquinas necesarias para que el sistema sea operativo.
	S: numero de maquinas de repuesto.
	M: numero de mecanicos.
	tf: tiempo medio de falla de una maquina
	tr: tiempo medio de reparacion de maquina.
	
	retorna:  el tiempo medio de funcionamiento del sistema
	
	
*/
double simulation(int N,int S,int M,double Tf,double Tr)
{

	int r = 0;								/*r indica el numero de maquinas para reparacion		*/
	double t = 0.0;							/*t es el tiempo total de funcionamiento del sistema 	*/ 
	double trep = INFINIT;					/* trep es t* : el tiempo de reparacion de una maquina	*/
	double t1 = 0.0;						/* t1 es el primer tiempo en fallar una maquin	a		*/
	
	int total_tiempos = N + S + 1 ;		 /* tiempos de fallas para las N maquinas en operacion +
												tiempos de fallas para las S maquinas para reposicion +
												un tiempo que indica el tiempo de reparacion: tr
											*/
	double X = 0.0	;						/*V.A.  no se  de que tipo*/
	double Y = 0.0;							/*V.A. nose de que tipo */								
	
	GQueue * tiempos = crear_tiempos(total_tiempos);
	assert(tiempos != NULL);
	/* SE cargann los tiempos de fallas para las maquinas*/
	cargar_tiempos(tiempos,Tf);
	
		
	while (r < S + 1) /*si r == S + 1 termina*/
	{
		
		t1 = obtener_menor_tiempo(tiempos);
		trep = obtener_tiempo_reparacion(tiempos);
		if (t1 < trep) /*caso 1: falla una maquina antes de que se repare otra*/
		{
			t += t1; /*TODO: revisar: no se si deber ir  t = t1 */
			++r; /*una maquina mas para reparacionr*/
			if(r == S + 1)
				break; /*el sistema falla por que ya has muchas maquinas en reparacion*/
			else
			{
				X = generar_exponencial(Tr);
				X += t;
				/*tiempos[total_tiempos-1] = X;*/
				/*TODO no entiendo bien como actualizar aca*/
				ordenar_tiempos(tiempos);
			}	
		}	
		else /*evento == REPARACION*/	
		{
			--r;/*una maquina mas que pasa a estar disponible para reppsicion en caso de falla*/
			t += trep;	/*TODO: revisar: no se si deber ir  t = trep */
			Y = generar_exponencial(Tr);
			if( r > 0)
				trep =  t + Y;
			else
			trep = INFINIT;				
		}	
		
	}	
	return t;
}		



int main (int argc, char **argv)
{
	int	 	i 		= 0;
	int 	N 		= 5; 	/* Numero de maquinas necesarias para que el sistema sea operativo */
	int 	S 		= 2; 	/* Numero de maquinas de repuesto*/
	int 	M 		= 1; 	/* Numero de mecanicos */
	double	Tf 		= 1;	/* Tiempo medio de fallo de una lavadora */
	double	Tr 		= 1/8;	/* Tiempo medio de raparaci贸n de una lavadora */
	double 	ft    	= 0.0;	/* Tiempo acumulado de fallos */
	double	ft2   	= 0.0;	/* Tiempo acumulado de fallosal cuadrado (para calcular varianza) */
	double 	E 		= 0.0; 	/* Esperanza*/
	double	V 		= 0.0; 	/* Varianza */
	double	dV 		= 0.0; 	/* desviacion estandar */
	int     num_simulaciones = 0;
	
	if(argc > 1)
	{
		num_simulaciones = argv[1];
	}
	else
	{
		printf("debe llamar al programa pasando el numero de simulaciones\n");
		return -1;
	}
	
	for (i = 0 ; i < num_simulaciones ; i++)
	{
		ft += simulation(N,S,M,Tf,Tr);
		ft2 += pow(ft,2);								/* para  calcular varianza*/
	}
	E  = ft/(double)num_simulaciones;					/* esperanza */
	V  = ft2/(double)num_simulaciones - pow(E,2);		/* varianza */
	dV = sqrt(V);										/* desviacion estandar */
	
	printf ("\nTiempo medio de fallo del sistema: \n%f",E);
	printf ("\ndesvacion estandar: \n%f",dV);

	return 0;
}
